using System;
using System.Security.Cryptography;
using System.Text;
using Org.BouncyCastle.Asn1;
using Org.BouncyCastle.Asn1.GM;
using Org.BouncyCastle.Asn1.Pkcs;
using Org.BouncyCastle.Asn1.X509;
using Org.BouncyCastle.Asn1.X9;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Engines;
using Org.BouncyCastle.Crypto.Generators;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Crypto.Signers;
using Org.BouncyCastle.Crypto.Digests;
using Org.BouncyCastle.Math;
using Org.BouncyCastle.Math.EC;
using Org.BouncyCastle.OpenSsl;
using Org.BouncyCastle.Pkcs;
using Org.BouncyCastle.Security;
using Org.BouncyCastle.X509;
using System.IO;

namespace JlPay.Sdk.Utils
{
    /// <summary>
    /// 国密工具类，提供SM2、SM3、SM4等算法的实现
    /// </summary>
    public static class SmUtils
    {
        // SM4密钥长度（字节）
        private const int SM4_KEY_LENGTH = 16;

        // SM2曲线参数
        private static readonly X9ECParameters SM2_CURVE_PARAMS = GMNamedCurves.GetByName("sm2p256v1");
        private static readonly ECDomainParameters SM2_DOMAIN_PARAMS = new ECDomainParameters(
            SM2_CURVE_PARAMS.Curve, SM2_CURVE_PARAMS.G, SM2_CURVE_PARAMS.N, SM2_CURVE_PARAMS.H);

        #region Base64编解码

        /// <summary>
        /// 将字节数组转为Base64字符串
        /// </summary>
        public static string ToBase64String(byte[] data)
        {
            return Convert.ToBase64String(data);
        }

        /// <summary>
        /// 将Base64字符串转为字节数组
        /// </summary>
        public static byte[] FromBase64String(string data)
        {
            return Convert.FromBase64String(data);
        }

        #endregion

        #region 十六进制编解码

        /// <summary>
        /// 将字节数组转为十六进制字符串
        /// </summary>
        public static string ToHexString(byte[] data)
        {
            return BitConverter.ToString(data).Replace("-", "").ToLower();
        }

        /// <summary>
        /// 将十六进制字符串转为字节数组
        /// </summary>
        public static byte[] FromHexString(string data)
        {
            if (data.Length % 2 != 0)
                throw new ArgumentException("十六进制字符串长度必须为偶数");

            byte[] result = new byte[data.Length / 2];
            for (int i = 0; i < data.Length; i += 2)
            {
                result[i / 2] = Convert.ToByte(data.Substring(i, 2), 16);
            }
            return result;
        }

        #endregion

        #region PEM格式密钥解析

        /// <summary>
        /// 解析公钥PEM格式为SM2公钥对象
        /// </summary>
        public static ECPublicKeyParameters ParsePublicKey(string publicKeyPem)
        {
            if (string.IsNullOrEmpty(publicKeyPem) || !publicKeyPem.StartsWith("-----BEGIN"))
            {
                throw new ArgumentException("公钥不是PEM格式: 必须以-----BEGIN开头");
            }

            if (!publicKeyPem.Contains("-----END"))
            {
                throw new ArgumentException("公钥PEM格式错误: 缺少-----END标记");
            }

            using (var reader = new StringReader(publicKeyPem))
            {
                var pemReader = new PemReader(reader);
                var pemObject = pemReader.ReadPemObject();
                
                if (pemObject == null)
                {
                    throw new ArgumentException("解析公钥PEM格式失败: 无法识别的PEM格式");
                }

                var publicKeyInfo = SubjectPublicKeyInfo.GetInstance(pemObject.Content);
                var publicKey = PublicKeyFactory.CreateKey(publicKeyInfo);
                
                if (!(publicKey is ECPublicKeyParameters ecPublicKey))
                {
                    throw new InvalidOperationException("解析的密钥不是SM2公钥");
                }

                return ecPublicKey;
            }
        }

        /// <summary>
        /// 解析公钥PEM格式为十六进制字符串
        /// </summary>
        public static string ParsePublicKeyToHex(string publicKeyPem)
        {
            var publicKey = ParsePublicKey(publicKeyPem);
            return PublicKeyToHex(publicKey);
        }

        /// <summary>
        /// 解析私钥PEM格式为SM2私钥对象
        /// </summary>
        public static ECPrivateKeyParameters ParsePrivateKey(string privateKeyPem)
        {
            if (string.IsNullOrEmpty(privateKeyPem) || !privateKeyPem.StartsWith("-----BEGIN"))
            {
                throw new ArgumentException("私钥不是PEM格式: 必须以-----BEGIN开头");
            }

            if (!privateKeyPem.Contains("-----END"))
            {
                throw new ArgumentException("私钥PEM格式错误: 缺少-----END标记");
            }

            using (var reader = new StringReader(privateKeyPem))
            {
                var pemReader = new PemReader(reader);
                var pemObject = pemReader.ReadPemObject();
                
                if (pemObject == null)
                {
                    throw new ArgumentException("解析私钥PEM格式失败: 无法识别的PEM格式");
                }

                var privateKey = PrivateKeyFactory.CreateKey(pemObject.Content);
                
                if (!(privateKey is ECPrivateKeyParameters ecPrivateKey))
                {
                    throw new InvalidOperationException("解析的密钥不是SM2私钥");
                }

                return ecPrivateKey;
            }
        }

        /// <summary>
        /// 解析私钥PEM格式为十六进制字符串
        /// </summary>
        public static string ParsePrivateKeyToHex(string privateKeyPem)
        {
            var privateKey = ParsePrivateKey(privateKeyPem);
            return PrivateKeyToHex(privateKey);
        }

        #endregion

        #region 密钥转换

        /// <summary>
        /// 将SM2公钥转换为十六进制字符串
        /// </summary>
        public static string PublicKeyToHex(ECPublicKeyParameters publicKey)
        {
            var point = publicKey.Q.Normalize();
            var x = point.XCoord.ToBigInteger().ToByteArrayUnsigned();
            var y = point.YCoord.ToBigInteger().ToByteArrayUnsigned();

            // 确保x和y是32字节长度
            var xPadded = new byte[32];
            var yPadded = new byte[32];

            Array.Copy(x, 0, xPadded, 32 - x.Length, x.Length);
            Array.Copy(y, 0, yPadded, 32 - y.Length, y.Length);

            var combined = new byte[64];
            Array.Copy(xPadded, 0, combined, 0, 32);
            Array.Copy(yPadded, 0, combined, 32, 32);

            return ToHexString(combined);
        }

        /// <summary>
        /// 将SM2私钥转换为十六进制字符串
        /// </summary>
        public static string PrivateKeyToHex(ECPrivateKeyParameters privateKey)
        {
            var d = privateKey.D.ToByteArrayUnsigned();
            
            // 确保d是32字节长度
            var dPadded = new byte[32];
            Array.Copy(d, 0, dPadded, 32 - d.Length, d.Length);

            return ToHexString(dPadded);
        }

        /// <summary>
        /// 将十六进制字符串转换为SM2私钥
        /// </summary>
        public static ECPrivateKeyParameters HexToPrivateKey(string hexKey)
        {
            var d = FromHexString(hexKey);
            var dInt = new BigInteger(1, d);
            
            return new ECPrivateKeyParameters(dInt, SM2_DOMAIN_PARAMS);
        }

        /// <summary>
        /// 将十六进制字符串转换为SM2公钥
        /// </summary>
        public static ECPublicKeyParameters HexToPublicKey(string hexKey)
        {
            if (hexKey.Length != 128)
            {
                throw new ArgumentException("无效的公钥十六进制字符串长度");
            }

            var xHex = hexKey.Substring(0, 64);
            var yHex = hexKey.Substring(64, 64);

            var x = FromHexString(xHex);
            var y = FromHexString(yHex);

            var xInt = new BigInteger(1, x);
            var yInt = new BigInteger(1, y);

            var point = SM2_CURVE_PARAMS.Curve.CreatePoint(xInt, yInt);
            
            return new ECPublicKeyParameters(point, SM2_DOMAIN_PARAMS);
        }

        #endregion

        #region SM2签名和验证

        /// <summary>
        /// SM2签名，返回Base64编码的签名值
        /// </summary>
        public static string SM2Sign(byte[] data, string privateKey)
        {
            var priKey = ConvertToPrivateKey(privateKey);
            
            var signer = new SM2Signer();
            signer.Init(true, priKey);
            signer.BlockUpdate(data, 0, data.Length);
            
            var signature = signer.GenerateSignature();
            return Convert.ToBase64String(signature);
        }

        /// <summary>
        /// SM2验签
        /// </summary>
        public static bool SM2Verify(byte[] data, string signature, string publicKey)
        {
            try
            {
                // 解码base64签名
                byte[] signBytes = FromBase64String(signature);

                var pubKey = ConvertToPublicKey(publicKey);
                
                var verifier = new SM2Signer();
                verifier.Init(false, pubKey);
                verifier.BlockUpdate(data, 0, data.Length);
                
                return verifier.VerifySignature(signBytes);
            }
            catch
            {
                return false;
            }
        }

        #endregion

        #region SM2加密和解密

        /// <summary>
        /// SM2加密（C1C3C2格式）
        /// </summary>
        public static byte[] SM2Encrypt(byte[] plaintext, string publicKey)
        {
            var pubKey = ConvertToPublicKey(publicKey);
            
            var engine = new SM2Engine(SM2Engine.Mode.C1C3C2);
            engine.Init(true, new ParametersWithRandom(pubKey, new SecureRandom()));
            
            return engine.ProcessBlock(plaintext, 0, plaintext.Length);
        }

        /// <summary>
        /// SM2解密（C1C3C2格式）
        /// </summary>
        public static byte[] SM2Decrypt(byte[] ciphertext, string privateKey)
        {
            var priKey = ConvertToPrivateKey(privateKey);
            
            var engine = new SM2Engine(SM2Engine.Mode.C1C3C2);
            engine.Init(false, priKey);
            
            return engine.ProcessBlock(ciphertext, 0, ciphertext.Length);
        }

        #endregion

        #region SM4加密和解密

        /// <summary>
        /// 生成SM4密钥
        /// </summary>
        public static byte[] GenerateSM4Key()
        {
            var random = new SecureRandom();
            var key = new byte[SM4_KEY_LENGTH];
            random.NextBytes(key);
            return key;
        }

        /// <summary>
        /// SM4/ECB/PKCS5Padding加密
        /// </summary>
        public static byte[] SM4ECBEncrypt(byte[] plaintext, byte[] key)
        {
            if (key.Length != SM4_KEY_LENGTH)
            {
                throw new ArgumentException($"SM4密钥长度必须为{SM4_KEY_LENGTH}字节");
            }

            var engine = new SM4Engine();
            var keyParam = new KeyParameter(key);
            
            // 添加PKCS5填充
            var paddedPlaintext = AddPKCS5Padding(plaintext, 16);
            
            engine.Init(true, keyParam);
            
            var ciphertext = new byte[paddedPlaintext.Length];
            for (int i = 0; i < paddedPlaintext.Length; i += 16)
            {
                engine.ProcessBlock(paddedPlaintext, i, ciphertext, i);
            }
            
            return ciphertext;
        }

        /// <summary>
        /// SM4/ECB/PKCS5Padding解密
        /// </summary>
        public static byte[] SM4ECBDecrypt(byte[] ciphertext, byte[] key)
        {
            if (key.Length != SM4_KEY_LENGTH)
            {
                throw new ArgumentException($"SM4密钥长度必须为{SM4_KEY_LENGTH}字节");
            }

            var engine = new SM4Engine();
            var keyParam = new KeyParameter(key);
            
            engine.Init(false, keyParam);
            
            var plaintext = new byte[ciphertext.Length];
            for (int i = 0; i < ciphertext.Length; i += 16)
            {
                engine.ProcessBlock(ciphertext, i, plaintext, i);
            }
            
            // 移除PKCS5填充
            return RemovePKCS5Padding(plaintext);
        }

        #endregion

        #region 摘要计算

        /// <summary>
        /// 读取随机数
        /// </summary>
        public static int ReadRandom(byte[] buffer)
        {
            var random = new SecureRandom();
            random.NextBytes(buffer);
            return buffer.Length;
        }

        /// <summary>
        /// 根据摘要算法类型生成摘要
        /// </summary>
        public static byte[] GetDigestByType(string digestType, byte[] data)
        {
            switch (digestType.ToUpper())
            {
                case "SM3":
                    var sm3 = new SM3Digest();
                    sm3.BlockUpdate(data, 0, data.Length);
                    var sm3Result = new byte[sm3.GetDigestSize()];
                    sm3.DoFinal(sm3Result, 0);
                    return sm3Result;
                    
                case "SHA256":
                    using (var sha256 = SHA256.Create())
                    {
                        return sha256.ComputeHash(data);
                    }
                    
                default:
                    throw new ArgumentException($"不支持的摘要算法类型: {digestType}");
            }
        }

        #endregion

        #region 公共方法（测试需要）

        /// <summary>
        /// 获取SM2域参数（供测试使用）
        /// </summary>
        public static ECDomainParameters GetSM2DomainParams()
        {
            return SM2_DOMAIN_PARAMS;
        }

        #endregion

        #region 私有方法

        /// <summary>
        /// 检查是否是PEM格式的密钥
        /// </summary>
        private static bool IsPEMFormat(string key)
        {
            return !string.IsNullOrEmpty(key) && key.Length > 10 && key.StartsWith("-----BEGIN");
        }

        /// <summary>
        /// 转换各种格式的私钥为SM2私钥对象
        /// </summary>
        private static ECPrivateKeyParameters ConvertToPrivateKey(string privateKey)
        {
            if (IsPEMFormat(privateKey))
            {
                return ParsePrivateKey(privateKey);
            }
            else
            {
                return HexToPrivateKey(privateKey);
            }
        }

        /// <summary>
        /// 转换各种格式的公钥为SM2公钥对象
        /// </summary>
        private static ECPublicKeyParameters ConvertToPublicKey(string publicKey)
        {
            if (IsPEMFormat(publicKey))
            {
                return ParsePublicKey(publicKey);
            }
            else
            {
                return HexToPublicKey(publicKey);
            }
        }

        /// <summary>
        /// 添加PKCS5填充
        /// </summary>
        private static byte[] AddPKCS5Padding(byte[] data, int blockSize)
        {
            int paddingLength = blockSize - (data.Length % blockSize);
            var paddedData = new byte[data.Length + paddingLength];
            Array.Copy(data, paddedData, data.Length);
            
            for (int i = data.Length; i < paddedData.Length; i++)
            {
                paddedData[i] = (byte)paddingLength;
            }
            
            return paddedData;
        }

        /// <summary>
        /// 移除PKCS5填充
        /// </summary>
        private static byte[] RemovePKCS5Padding(byte[] paddedData)
        {
            if (paddedData.Length == 0)
                return paddedData;
                
            int paddingLength = paddedData[paddedData.Length - 1];
            
            if (paddingLength <= 0 || paddingLength > paddedData.Length)
                return paddedData;
                
            var data = new byte[paddedData.Length - paddingLength];
            Array.Copy(paddedData, data, data.Length);
            
            return data;
        }

        #endregion
    }
} 